/**********************************************************************
 *** *                                                            * ***
 *** *  Copyright (c) 2013 Harmonia Holdings Group LLC            * ***
 *** *                                                            * ***
 *** * Permission is hereby granted to use, modify, copy, and     * ***
 *** * distribute this software and its documentation for any     * ***
 *** * purpose only without profit, provided that the above       * ***
 *** * Copyright Notice appears in all copies and that both the   * ***
 *** * Copyright Notice and this Permission Notice appears in     * ***
 *** * every copy of supporting documentation.  No title to nor   * ***
 *** * ownership of the software is transferred hereby.  The name * ***
 *** * of Harmonia Holdings Group LLC shall not be used in        * ***
 *** * advertising or publicity related to the distribution of    * ***
 *** * the software without specific, written, prior permission.  * ***
 *** * This software is provided as-delivered without expressed   * ***
 *** * or implied warranty.  Harmonia Holdings Group LLC          * ***
 *** * makes no representation about the suitability of this      * ***
 *** * software for any purpose and accepts no responsibility for * ***
 *** * its use.                                                   * ***
 *** *                                                            * ***
 *** ************************************************************** ***
 *** *                                                            * ***
 *** * This program is free software; you can redistribute it     * ***
 *** * and/or modify it under the terms of the GNU General Public * ***
 *** * License as published by the Free Software Foundation;      * ***
 *** * either version 2 of the License, or (at your option) any   * ***
 *** * later version.                                             * ***
 *** *                                                            * ***
 *** * This program is distributed in the hope that it will be    * ***
 *** * useful, but WITHOUT ANY WARRANTY; without even the implied * ***
 *** * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR    * ***
 *** * PURPOSE.  See the GNU General Public License for more      * ***
 *** * details.                                                   * ***
 *** *                                                            * ***
 *** * You should have received a copy of the GNU General Public  * ***
 *** * License along with this program; if not, write to the Free * ***
 *** * Software Foundation, Inc., 51 Franklin Street, Fifth       * ***
 *** * Floor, Boston, MA 02110-1301, USA.                         * ***
 *** *                                                            * ***
 *** * For more information: http://www.gnu.org/licenses/gpl.html * ***
 *** *                                                            * ***
 **********************************************************************/
package org.etexascode.interrep.datamodel;
import java.awt.Polygon;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;

import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.etexascode.interrep.datamodel.interfaces.IDistanceable;
import org.etexascode.interrep.datamodel.utils.UtilsSpecialEquals;
import org.etexascode.interrep.datamodel.utils.UtilsStringOnModel;

/**
 * The detector model.
 * @author unknown
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
@XmlSeeAlso(DetectorEvent.class)
public class Detector implements Serializable, IDistanceable {
	/** Serial ID */
	@XmlTransient
	private static final long serialVersionUID = 2999196670024338084L;


	/** Detector ID. */
	@XmlElement
	private int detectorID;

	/** List of lane IDs. */
	@XmlElement
	private List<Integer> laneIDs;

	/** Is presence detector. */
	@XmlElement
	private boolean presenceDetectCap;

	/** Is pulse detector. */
	@XmlElement
	private boolean pulseDetectCap;

	/** Is speed detector. */
	@XmlElement
	private boolean speedDetectCap;

	/** Is length detector. */
	@XmlElement
	private boolean lengthDetectCap;

	/** The detector event. */
	@XmlElement
	private DetectorEvent detEvent;

	/** The area of the detector. */
	@XmlElement
	private Polygon area;


	/** Constructor. */
	public Detector() {
		detectorID = 0;
		laneIDs = new ArrayList<Integer>();
		presenceDetectCap = false;
		pulseDetectCap = false;
		speedDetectCap = false;
		lengthDetectCap = false;
		detEvent = new DetectorEvent();
		area = new Polygon();
	}

	/**
	 * Clone the area field of this class.
	 * @return The clone of the area.
	 */
	public Polygon cloneArea() {
		return SerializationUtils.clone(area);
	}

	/**
	 * Standard equality operator for the detector.
	 * @param obj The detector to check this detector against.
	 * @return Is this detector the same as d?
	 */
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Detector) {
			Detector d = (Detector) obj;
			if(!(detectorID == d.detectorID)) {
				return false;
			} else if(!(presenceDetectCap == d.presenceDetectCap)) {
				return false;
			} else if(!(pulseDetectCap == d.pulseDetectCap)) {
				return false;
			} else if(!(speedDetectCap == d.speedDetectCap)) {
				return false;
			} else if(!(lengthDetectCap == d.lengthDetectCap)) {
				return false;
			} else if(!(laneIDs.size() == d.laneIDs.size())) {
				return false;
			} else if(!laneIDs.containsAll(d.laneIDs)) {
				return false;
			} else if(!d.laneIDs.containsAll(laneIDs)) {
				return false;
			} else if(!UtilsSpecialEquals.equals(area, d.getArea())) {
				return false;
			}
            
            if(detEvent == null) {
                if(d.detEvent == null) {
                    return true;
                } else {
                    return false;
                }
            } else if(!detEvent.equals(d.detEvent)) {
                return false;
            }
            
			return true;
		} else {
			return false;
		}
	}

	/**
	 * HashCode operation. 
	 * @return A hashcode of the data. 
	 */
	@Override
	public int hashCode() {
		return new HashCodeBuilder(63, 17).
				append(detectorID).
				append(detEvent).
				toHashCode();
	}

	/**
	 * Gets the detector ID.
	 * @return Detector ID.
	 */
	public int getDetectorID() {
		return detectorID;
	}

	/**
	 * Sets the detector ID.
	 * @param detectorID The new detector ID.
	 */
	public void setDetectorID(int detectorID) {
		this.detectorID = detectorID;
	}

	/**
	 * Gets the lane IDs.
	 * @return List of lane ID's.
	 */
	public List<Integer> getLaneIDs() {
		return laneIDs;
	}

	/**
	 * Sets the lane IDs.
	 * @param laneIDs The new list of lane IDs.
	 */
	public void setLaneIDs(List<Integer> laneIDs) {
		this.laneIDs = laneIDs;
	}

	/**
	 * Is this a presence detector.
	 * @return True/False
	 */
	public boolean isPresenceDetectCap() {
		return presenceDetectCap;
	}

	/**
	 * Set if this is a presence detector.
	 * @param presenceDetectCap True/False
	 */
	public void setPresenceDetectCap(boolean presenceDetectCap) {
		this.presenceDetectCap = presenceDetectCap;
	}

	/**
	 * Is this a pulse detector.
	 * @return True/False
	 */
	public boolean isPulseDetectCap() {
		return pulseDetectCap;
	}

	/**
	 * Set if this is a pulse detector.
	 * @param pulseDetectCap True/False
	 */
	public void setPulseDetectCap(boolean pulseDetectCap) {
		this.pulseDetectCap = pulseDetectCap;
	}

	/**
	 * Is this a speed detector.
	 * @return True/False
	 */
	public boolean isSpeedDetectCap() {
		return speedDetectCap;
	}

	/**
	 * Sets if this is a speed detector.
	 * @param speedDetectCap True/False
	 */
	public void setSpeedDetectCap(boolean speedDetectCap) {
		this.speedDetectCap = speedDetectCap;
	}

	/**
	 * Is this a length detector.
	 * @return True/False
	 */
	public boolean isLengthDetectCap() {
		return lengthDetectCap;
	}

	/**
	 * Sets if this is a length detector.
	 * @param lengthDetectCap True/False
	 */
	public void setLengthDetectCap(boolean lengthDetectCap) {
		this.lengthDetectCap = lengthDetectCap;
	}

	/**
	 * Gets the detector event.
	 * @return The detector event.
	 */
	public DetectorEvent getDetEvent() {
		return detEvent;
	}

	/**
	 * Sets the detector event.
	 * @param detEvent The new detector event.
	 */
	public void setDetEvent(DetectorEvent detEvent) {
		this.detEvent = detEvent;
	}

	/**
	 * Gets the area of the detector.
	 * @return The area.
	 */
	public Polygon getArea() {
		return area;
	}

	/**
	 * Sets the area of the detector.
	 * @param area The new area.
	 */
	public void setArea(Polygon area) {
		this.area = area;
	}

	/** The string representation of the detector. */
	@Override
	public String toString() {
		StringBuilder ret = new StringBuilder();

		ret.append("Detector Id = ");
		ret.append(detectorID);
		ret.append("\n");

		ret.append("Presence Detection Cap = ");
		ret.append(presenceDetectCap);
		ret.append("\n");

		ret.append("Pulse Detect Cap = ");
		ret.append(pulseDetectCap);
		ret.append("\n");

		ret.append("Speed Detect Cap = ");
		ret.append(speedDetectCap);
		ret.append("\n");

		ret.append("Length Detect Cap = ");
		ret.append(lengthDetectCap);
		ret.append("\n");

		ret.append(laneIDsString());
		ret.append(UtilsStringOnModel.buildPolygonString(area));

		if(detEvent != null) {
			ret.append("Current Event:\n");
			ret.append(detEvent.toString());
			ret.append("\n");
		} else {
			ret.append("Current Event:\nnull\n");
		}

		return ret.toString();
	}

	/**
	 * Gets the lane IDs string.
	 * @return The lane IDs to string.
	 */
	private String laneIDsString() {
		StringBuilder ret = new StringBuilder("Lane IDs = {");

		for(int i = 0; i < laneIDs.size(); i++) {
			ret.append(laneIDs.get(i).toString());
			if(i != laneIDs.size()-1) {
				ret.append(", ");
			}
		}
		ret.append("}\n");

		return ret.toString();
	}

	@Override
	public double getX() {
		double ret = 0.0;
		for(int i = 0; i < area.npoints; i++) {
			ret += area.xpoints[i];
		}
		
		return ret/area.npoints;
	}

	@Override
	public double getY() {
		double ret = 0.0;
		for(int i = 0; i < area.npoints; i++) {
			ret += area.ypoints[i];
		}
		
		return ret/area.npoints;
	}
}